#include "graph_support/alg_correctness_tests/dinic_alg_tests/CompactNet.h"

#include <algorithm>
#include <assert.h>

#include "graph_support/attributes/FlowEdgeAttributes.h"
#include "graph_support/attributes/CostFlowEdgeAttributes.h"
#include "graph_support/alg_correctness_tests/generate_graph/FlowEdgeAttributes4UnitTests.h"


using namespace GRAPH_SUPPORT_NS;
using namespace GRAPH_SUPPORT_NS::attributes;
using namespace generate_graph;
using namespace dinic_alg_test;

typedef IGraph::EdgeAttributesImpl< FlowEdgeAttributes4UnitTests >::IAttributes IFlowEdgeAttributes4;
typedef IGraph::EdgeAttributesImpl< CostFlowEdgeAttributesTmpl< double > >::IAttributes ICostFlowEdgeAttributes;



double CompactNet::NaN = 0.0 / 0.0;

CompactNet::CompactNet( int vertCount, int edgCount, bool hasCost )
	: stBuf( vertCount )
	, nxBuf( edgCount * 2 )
	, aBuf( edgCount * 2 )
	, capBuf( edgCount * 2 )
	, flowBuf( edgCount * 2 )
	, costBuf( hasCost ? edgCount * 2 : 0 )
	, k( 0 )
{
	st = & stBuf[0];
	nx = & nxBuf[0];
	a = & aBuf[0];
	cap = & capBuf[0];
	flow = & flowBuf[0];
	cost = hasCost ? & costBuf[0] : NULL;
	std::fill( st, st + vertCount, -1 );
}

int CompactNet::getVertCount()
{
	return stBuf.size();
}

int CompactNet::getEdgCount()
{
	return aBuf.size() / 2;
}

void CompactNet::addEdge( int a, int b, double capacity )
{
	addSingleEdge( a, b, capacity, NaN );
	addSingleEdge( b, a, 0.0, NaN );
}

void CompactNet::addEdge( int a, int b, double capacity, double cost )
{
	assert( this->cost != NULL );
	addSingleEdge( a, b, capacity, cost );
	addSingleEdge( b, a, 0.0, -cost );
}

// TODO: rename : remainCapacity
double CompactNet::remainFlow( int edge )
{
	return cap[edge] - flow[edge];
}

void CompactNet::clear()
{
	k = 0;
	std::fill( st, st + stBuf.size(), -1 );
}

void CompactNet::addSingleEdge( int a, int b, double capacity, double cost )
{
	assert( k < nxBuf.size() );
	nx[k] = st[a];
	this->a[k] = b;
	st[a] = k;
	cap[k] = capacity;
	flow[k] = 0;
	if ( this->cost != NULL )
	{
		this->cost[k] = cost;
	}
	k++;
}


void dinic_alg_test::cpgr2compact( IGraph & gr, CompactNet & d, double eps )
{
	// TODO: graph drawer UI
	int n = gr.igetVertexCount();
	IGraph::IEdgeIteratorPtr eitPtr = gr.igetOutEdgesIterator();
	IGraph::IEdgeIterator & eit = * eitPtr;
	for ( int i = 0; i < n; i++ )
	{
		for ( gr.igetOutEdgesIterator( i, eit ); eit.notFinished(); eit.next() )
		{
			// TODO: utility method 4 it
			IGraph::IEdgeAttributesPtr attrsPtr = eit.getEdgeAttributes();
			IFlowEdgeAttributes4 * fawp = dynamic_cast< IFlowEdgeAttributes4 * >( attrsPtr.get() );
			ICostFlowEdgeAttributes * cfawp = dynamic_cast< ICostFlowEdgeAttributes * >( attrsPtr.get() );
			assert( fawp != NULL || cfawp != NULL );
			int jd = eit.getDestination();
			if ( cfawp != NULL )
			{
				double cap = cfawp->get()->getCapacity();
				if ( cap < eps )
				{
					continue;
				}
				double cost = cfawp->get()->getCost();
				d.addEdge( i, jd, cap, cost );
			} else
			{
				double cap = fawp->get()->getCapacity();
				if ( cap < eps )
				{
					continue;
				}
				d.addEdge( i, jd, cap );
			}
		}
	}
}

CompactNetPtr dinic_alg_test::clonegr2compact( IGraph & gr, bool hasCost )
{
	using namespace GRAPH_SUPPORT_NS; // TODO: move 2 .cpp

	int vertsCount = gr.igetVertexCount();
	int edgesCount = 0;
	int n = gr.igetVertexCount();
	IGraph::IEdgeIteratorPtr eitPtr = gr.igetOutEdgesIterator();
	IGraph::IEdgeIterator & eit = * eitPtr;
	for ( int i = 0; i < n; i++ )
	{
		for ( gr.igetOutEdgesIterator( i, eit ); eit.notFinished(); eit.next() )
		{
			edgesCount++;
		}
	}
	CompactNetPtr result( new CompactNet( vertsCount, edgesCount, hasCost ) );
	cpgr2compact( gr, * result );
	return result;
}
